package com.example.leave.service.impl;

import com.example.leave.common.CommonPage;
import com.example.leave.common.DateTimeUtil;
import com.example.leave.common.JwtUtil;
import com.example.leave.common.PageUtil;
import com.example.leave.component.equator.Equator;
import com.example.leave.component.equator.FieldInfo;
import com.example.leave.component.equator.GetterBaseEquator;
import com.example.leave.dao.LeaveRepository;
import com.example.leave.dto.LeaveFormDto;
import com.example.leave.entity.LeaveForm;
import com.example.leave.exception.BizException;
import com.example.leave.exception.ExceptionEnum;
import com.example.leave.form.*;
import com.example.leave.pojo.StartEndTimestamp;
import com.example.leave.service.LeaveFormOperationHistoryService;
import com.example.leave.service.LeaveService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.sql.Timestamp;
import java.util.List;
import java.util.Optional;

@Service
public class LeaveServiceImpl implements LeaveService {

    @Autowired
    LeaveRepository leaveRepository;
    @Autowired
    LeaveFormOperationHistoryService leaveFormOperationHistoryService;

    @Override
    public CommonPage<LeaveFormDto> getAll(PageInfoForm pageInfoForm) {
        Page<LeaveForm> leaveFormPage = leaveRepository.findAll(PageUtil.pageInfoToPageable(pageInfoForm));
        return CommonPage.getCommonPage(leaveFormPage, pageInfoForm.getSort());
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    @Override
    public boolean add(LeaveFormForm leaveFormForm) {
        try {
            IdAndTimeBetweenForm idAndTimeBetweenForm = new IdAndTimeBetweenForm();
            idAndTimeBetweenForm.setEnd(leaveFormForm.getEndDateTime());
            idAndTimeBetweenForm.setStart(leaveFormForm.getStartDateTime());
            idAndTimeBetweenForm.setId(JwtUtil.getUserFromToken().getId());
            if (isSomeoneHasLeaveAtSometime(idAndTimeBetweenForm)) {
                return false;
            }
            LeaveForm leaveForm = new LeaveForm(leaveFormForm);
            leaveFormOperationHistoryService.addOperation(leaveForm);
            leaveRepository.save(leaveForm);
            return true;

        } catch (Exception e) {
            throw new BizException(ExceptionEnum.INSERT_ERROR, e);
        }

    }

    @Override
    public boolean delOneById(String id) {
        try {
            leaveRepository.deleteById(id);
            leaveFormOperationHistoryService.deleteOperation(id);
            return true;
        } catch (Exception e) {
            throw e;
        }

    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    @Override
    public int delByIdList(List<String> ids) {
        for (String id : ids) {
            if (leaveRepository.existsById(id)) {
                leaveFormOperationHistoryService.deleteOperation(id);
            }
        }
        return leaveRepository.deleteByIdIn(ids);

    }

    @Override
    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public List<FieldInfo> updateById(IdAndLeaveFormForm idAndLeaveFormForm) {
        LeaveForm newLeaveForm = new LeaveForm(idAndLeaveFormForm.getLeaveFormForm());
        LeaveForm oldLeaveForm = leaveRepository.findById(idAndLeaveFormForm.getLeaveFormId())
                .orElse(null);
        if (null == oldLeaveForm) {
            throw new BizException("该请假表单不存在，无法更新！");
        }

        newLeaveForm.setId(idAndLeaveFormForm.getLeaveFormId());
        newLeaveForm.setCreateTime(oldLeaveForm.getCreateTime());
        Equator equator = new GetterBaseEquator();
        if (equator.isEquals(newLeaveForm, oldLeaveForm)) {
            throw new BizException("提交信息无更改！");
        }
        List<FieldInfo> diff = equator.getDiffFields(
                newLeaveForm, oldLeaveForm);
        leaveFormOperationHistoryService.updateOperation(newLeaveForm);
        leaveRepository.save(newLeaveForm);
        return diff;

    }

    @Override
    public CommonPage<LeaveFormDto> findWhoWasAbsentAtSometime(TimeConditionForm timeConditionForm) {
        StartEndTimestamp startEndTimestamp = DateTimeUtil
                .startEndDateTimeVoToStartEndTimestamp(timeConditionForm.getStartEndDateTimeForm());
        Pageable pageable = PageUtil.pageInfoToPageable(timeConditionForm.getPageInfoForm());
        Page<LeaveForm> leaveFormPage = leaveRepository.findAll((Specification<LeaveForm>)
                (root, criteriaQuery, criteriaBuilder) -> {
                    Predicate predicate = criteriaBuilder.conjunction();
                    predicate.getExpressions().add(criteriaBuilder.lessThan(
                            root.get("startTime"), startEndTimestamp.getEndTime()));
                    predicate.getExpressions().add(criteriaBuilder.greaterThan(
                            root.get("endTime"), startEndTimestamp.getStartTime()));
                    return predicate;
                }, pageable);

        return CommonPage.getCommonPage(leaveFormPage, timeConditionForm.getPageInfoForm().getSort());
    }

    @Override
    public boolean isSomeoneHasLeaveAtSometime(IdAndTimeBetweenForm userIdAndTimeBetweenForm) {
        Timestamp start = DateTimeUtil.dateTimeToTimestamp(userIdAndTimeBetweenForm.getStart());
        Timestamp end = DateTimeUtil.dateTimeToTimestamp(userIdAndTimeBetweenForm.getEnd());

        List<LeaveForm> leaveFormList = leaveRepository.findAll((Specification<LeaveForm>)
                (root, criteriaQuery, criteriaBuilder) -> {
                    Predicate predicate = criteriaBuilder.conjunction();
                    predicate.getExpressions().add(criteriaBuilder.lessThan(
                            root.get("startTime"), end));
                    predicate.getExpressions().add(criteriaBuilder.greaterThan(
                            root.get("endTime"), start));
                    predicate.getExpressions().add(criteriaBuilder.equal(root.get("userId"), userIdAndTimeBetweenForm.getId()));
                    return predicate;
                });
        return !leaveFormList.isEmpty();
    }


    @Override
    public CommonPage<LeaveFormDto> findByCategoryLike(ConditionAndPageInfoForm categoryAndPageInfoVo) {
        Page<LeaveForm> leaveFormPage = leaveRepository.findByCategoryLike(categoryAndPageInfoVo.getCondition(),
                PageUtil.pageInfoToPageable(categoryAndPageInfoVo.getPageInfoForm()));
        return CommonPage.getCommonPage(leaveFormPage, categoryAndPageInfoVo.getPageInfoForm().getSort());
    }

    @Override
    public CommonPage<LeaveFormDto> findByReasonLike(ConditionAndPageInfoForm ReasonAndPageInfoVo) {
        Page<LeaveForm> leaveFormPage = leaveRepository.findByReasonLike(ReasonAndPageInfoVo.getCondition(),
                PageUtil.pageInfoToPageable(ReasonAndPageInfoVo.getPageInfoForm()));
        return CommonPage.getCommonPage(leaveFormPage, ReasonAndPageInfoVo.getPageInfoForm().getSort());
    }


    @Override
    public LeaveFormDto findById(String id) {
        LeaveFormDto leaveFormDto = new LeaveFormDto();
        Optional<LeaveForm> leaveFormOptional = leaveRepository.findById(id);
        if (leaveFormOptional.isPresent()) {
            BeanUtils.copyProperties(leaveFormOptional.get(), leaveFormDto);
            return leaveFormDto;
        }
        return null;

    }

    @Override
    public CommonPage<LeaveFormDto> findByUserIdAndStartTimeBetween(IdAndTimeBetweenAndPageInfoVoForm userIdAndTimeBetweenAndPageInfoVo) {

        Page<LeaveForm> leaveFormPage = leaveRepository.findByUserIdAndStartTimeBetween(
                userIdAndTimeBetweenAndPageInfoVo.getId(),
                DateTimeUtil.dateTimeToTimestamp(userIdAndTimeBetweenAndPageInfoVo.getStart()),
                DateTimeUtil.dateTimeToTimestamp(userIdAndTimeBetweenAndPageInfoVo.getEnd()),
                PageUtil.pageInfoToPageable(userIdAndTimeBetweenAndPageInfoVo.getPageInfoForm()));
        return CommonPage.getCommonPage(leaveFormPage, userIdAndTimeBetweenAndPageInfoVo.getPageInfoForm().getSort());
    }

    @Override
    public CommonPage<LeaveFormDto> findByStartTimeBetween(TimeBetweenAndPageInfoForm startTimeBetweenAndPageInfoForm) {
        Page<LeaveForm> leaveFormPage = leaveRepository.findByStartTimeBetween(
                DateTimeUtil.dateTimeToTimestamp(startTimeBetweenAndPageInfoForm.getStart()),
                DateTimeUtil.dateTimeToTimestamp(startTimeBetweenAndPageInfoForm.getEnd()),
                PageUtil.pageInfoToPageable(startTimeBetweenAndPageInfoForm.getPageInfoForm()));
        return CommonPage.getCommonPage(leaveFormPage, startTimeBetweenAndPageInfoForm.getPageInfoForm().getSort());
    }


}
